package com.doctcloud.datacloud.service;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doctcloud.common.core.utils.bean.BeanUtils;
import com.doctcloud.datacloud.entity.InpBillDetailEntity;
import com.doctcloud.datacloud.mapper.InpBillDetailEntityMapper;
import com.doctcloud.hisinfo.api.RemoteHisInpBillService;
import com.doctcloud.hisinfo.histable.domain.inpbill.InpBillDetailDo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class InpBillDetailEntityService extends ServiceImpl<InpBillDetailEntityMapper, InpBillDetailEntity> {

    @Value("${sync.batch.size:500}") // 提供默认值，增强健壮性
    private int BATCH_SIZE;

    private final RemoteHisInpBillService remoteHisInpBillService;

    /**
     * 同步指定患者、指定住院记录的住院费用明细信息（分批处理优化）
     * 增加了 @Transactional 注解，确保数据操作的原子性。
     *
     * @param patientId 患者ID
     * @param visitId   住院标识
     */
    @Transactional
    public void syncPat(String patientId, Integer visitId) {
        log.info("--- 开始同步患者ID [{}], 住院标识 [{}] 的住院费用明细数据 ---", patientId, visitId);
        long startTime = System.currentTimeMillis();

        try {
            // 1. 从远程服务获取数据，并统一处理为非null列表
            List<InpBillDetailDo> remoteInpLists = remoteHisInpBillService.listByPat(patientId, visitId);
            // 使用 CollectionUtils.isEmpty 进行判断，更安全
            if (CollectionUtils.isEmpty(remoteInpLists)) {
                log.warn("从远程服务获取患者ID [{}], 住院标识 [{}] 的住院费用明细信息为空，同步任务终止。", patientId, visitId);
                return;
            }
            log.info("从远程服务获取到 {} 条住院费用明细信息。", remoteInpLists.size());

            // 2. 数据转换与过滤
            List<InpBillDetailEntity> entitiesToUpsert = remoteInpLists.stream()
                    .filter(this::isValidInpBillDetail) // 将过滤逻辑提取到独立方法
                    .map(this::convertToEntity)         // 将转换逻辑提取到独立方法
                    .collect(Collectors.toList());

            if (CollectionUtils.isEmpty(entitiesToUpsert)) {
                log.info("患者ID [{}], 住院标识 [{}] 没有需要同步的有效住院费用明细记录。", patientId, visitId);
                return;
            }
            log.info("准备执行批量UPSERT操作，共 {} 条记录，分批大小：{}。", entitiesToUpsert.size(), BATCH_SIZE);

            // 3. 执行分批批量UPSERT操作
            int totalAffectedRows = 0;
            int totalBatches = (int) Math.ceil((double) entitiesToUpsert.size() / BATCH_SIZE);

            for (int i = 0; i < totalBatches; i++) {
                int startIndex = i * BATCH_SIZE;
                int endIndex = Math.min(startIndex + BATCH_SIZE, entitiesToUpsert.size());
                List<InpBillDetailEntity> batchEntities = entitiesToUpsert.subList(startIndex, endIndex);

                log.debug("正在执行第 {} 批/共 {} 批，处理 {} 条记录。", i + 1, totalBatches, batchEntities.size());
                int affectedRows = baseMapper.batchUpsert(batchEntities);
                totalAffectedRows += affectedRows;
            }

            log.info("--- 同步完成。患者ID [{}], 住院标识 [{}]。总耗时：{} ms。数据库受影响的总行数: {} ---",
                    patientId, visitId, System.currentTimeMillis() - startTime, totalAffectedRows);

        } catch (Exception e) {
            // 异常日志中包含关键信息 patientId 和 visitId
            log.error("同步患者ID [{}], 住院标识 [{}] 的住院费用明细信息时发生异常: ", patientId, visitId, e);
            // 异常会自动触发事务回滚
            // 建议向上抛出异常，让调用方知道发生了错误
            throw e;
        }
    }

    /**
     * 校验 InpBillDetailDo 对象是否有效。
     * 提取出来后，主流程更清晰，也便于单独测试。
     */
    private boolean isValidInpBillDetail(InpBillDetailDo doObj) {
        // 可以根据业务需求调整校验规则
        return doObj != null &&
                doObj.getPatientId() != null && !doObj.getPatientId().trim().isEmpty() &&
                doObj.getVisitId() != null &&
                doObj.getItemClass() != null && !doObj.getItemClass().trim().isEmpty() &&
                doObj.getItemCode() != null && !doObj.getItemCode().trim().isEmpty() &&
                doObj.getItemSpec() != null && !doObj.getItemSpec().trim().isEmpty();
    }

    /**
     * 将 InpBillDetailDo 转换为 InpBillDetailEntity。
     * 统一的转换逻辑入口，便于维护和修改。
     */
    private InpBillDetailEntity convertToEntity(InpBillDetailDo doObj) {
        InpBillDetailEntity entity = new InpBillDetailEntity();
        BeanUtils.copyProperties(doObj, entity);
        return entity;
    }
}